/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2021 Marcus Britanicus (https://gitlab.com/marcusbritanicus)
 * Copyright (c) 2021 Abrar (https://gitlab.com/s96Abrar)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 **/

#include <QtCore>
#include <QtGui>

#include <unistd.h>
#include <sys/select.h>
#include <wayland-client.h>

#include <wayqt/WayQtUtils.hpp>
#include <wayqt/Registry.hpp>
#include <wayqt/DataControl.hpp>

WQt::MimeData gatherOfferedData( WQt::DataControlOffer *offer ) {
    WQt::MimeData mimeData;

    if ( offer and offer->isValid() ) {
        QStringList mimeTypes( offer->offeredMimeTypes() );
        QByteArray  previous;
        for ( QString mt: mimeTypes ) {
            QByteArray data = offer->retrieveData( mt );

            /** We have some valid data */
            if ( data.count() ) {
                mimeData.setData( mt, data );
                qDebug() << "  " << mt << data.constData();
                previous = data;
            }

            /** We have previously retrieved data */
            else if ( previous.count() ) {
                mimeData.setData( mt, data );
                qDebug() << "  " << mt << data.constData();
                previous = data;
            }
        }

        QObject::connect(
            offer, &WQt::DataControlOffer::invalidated, [ = ] () {
                offer->disconnect();
            }
        );
    }

    return mimeData;
}


int main( int argc, char *argv[] ) {
    QGuiApplication *app = new QGuiApplication( argc, argv );

    WQt::Registry *reg = new WQt::Registry( WQt::Wayland::display() );

    reg->setup();

    WQt::DataControlManager *dcm     = reg->dataControlManager();
    WQt::DataControlDevice  *dataDev = dcm->getDataDevice( reg->waylandSeat() );

    /** MimeData storage for empty clipboard prevention */
    WQt::MimeData clpData;
    WQt::MimeData selData;

    QObject::connect(
        dataDev, &WQt::DataControlDevice::selectionOffered, [ = ] ( WQt::DataControlOffer *offer ) mutable {
            /** Gather this only if it's not our own */
            if ( !offer->offeredMimeTypes().contains( "application/x-demo-clipboard" ) ) {
                /** Alien data offer. Gather it. */
                qDebug() << "\nNew Selection";
                WQt::MimeData mimeData = gatherOfferedData( offer );
                qDebug() << "Gather";

                /**
                 * Empty clipboard prevention:
                 * Copy the mimeData to lastClipboard only if
                 * it was not empty.
                 */
                if ( mimeData.formats().count() ) {
                    clpData = mimeData;
                }

                clpData.setData( "application/x-demo-clipboard", "1" );

                /** Create a new data source, and set the mimeData */
                WQt::DataControlSource *clpSrc = dcm->createDataSource();
                qDebug() << "New source";
                clpSrc->setSelectionData( clpData );
                qDebug() << "Set data";

                /** Offer the mimeTypes */
                clpSrc->offer( "application/x-demo-clipboard" );
                for ( QString fmt: mimeData.formats() ) {
                    clpSrc->offer( fmt );
                }
                qDebug() << "Offer";

                /** Set this source as the new selection to the data_device */
                dataDev->setSelection( clpSrc );
                qDebug() << "Hijack Selection Source";
            }
        }
    );

    QObject::connect(
        dataDev, &WQt::DataControlDevice::primarySelectionOffered, [ = ] ( WQt::DataControlOffer *offer ) mutable {
            /** Gather this only if it's not our own */
            if ( !offer->offeredMimeTypes().contains( "application/x-demo-clipboard" ) ) {
                /** Alien data offer. Gather it. */
                qDebug() << "\nNew Primary";
                WQt::MimeData mimeData = gatherOfferedData( offer );
                qDebug() << "Gather";

                /**
                 * Empty clipboard prevention:
                 * Copy the mimeData to lastClipboard only if
                 * it was not empty.
                 */
                if ( mimeData.formats().count() ) {
                    selData = mimeData;
                }

                selData.setData( "application/x-demo-clipboard", "1" );

                /** Create a new data source, and set the mimeData */
                WQt::DataControlSource *selSrc = dcm->createDataSource();
                qDebug() << "New source";
                selSrc->setSelectionData( selData );
                qDebug() << "Set data";

                /** Offer the mimeTypes */
                selSrc->offer( "application/x-demo-clipboard" );
                for ( QString fmt: mimeData.formats() ) {
                    selSrc->offer( fmt );
                }
                qDebug() << "Offer";

                /** Set this source as the new selection to the data_device */
                dataDev->setPrimarySelection( selSrc );
                qDebug() << "Hijack Primary Source";
            }
        }
    );

    dataDev->setup();

    qDebug() << "Monitoring...";

    return app->exec();
}
